//
// Created by DengLibin on 2018/12/12 0012.
//
/**
多态:

C++中通过virtual关键字对多态进行支持
        使用virtual声明的函数被重写后即可展现多态特性

多态实现的三个条件
        有继承、有virtual重写、有父类指针（引用）指向子类对象

        基类是虚函数，子类复写了基类的虚函数，子类复写的这个函数也是虚函数（virtual关键字可省略），虚析构函数同理

        如果要通过父类指针去删除子类对象，那么父类析构函数也要声明为虚析构函数。

        virtual关键字，告诉编译器这个函数要支持多态；不是根据指针类型判断如何调用；
而是要根据指针所指向的实际对象类型来判断如何调用

        多态的理论基础
动态联编PK静态联编。根据实际的对象类型来判断重写函数的调用。

多态的重要意义
        设计模式的基础 是框架的基石。

多态原理探究
        理论知识：
当类中声明虚函数时，编译器会在类中生成一个虚函数表
        虚函数表是一个存储类成员函数指针的数据结构
虚函数表是由编译器自动生成与维护的
        virtual成员函数会被编译器放入虚函数表中
当存在虚函数时，每个对象中都有一个指向虚函数表的指针（C++编译器给父类对象、子类对象提前布局vptr指针；
当进行howToPrint(Parent *base)函数是，C++编译器不需要区分子类对象或者父类对象，只需要再base指针中，找vptr指针即可。

说明1：
通过虚函数表指针VPTR调用重写函数是在程序运行时进行的，因此需要通过寻址操作才能确定真正应该调用的函数。
而普通成员函数是在编译时就确定了调用的函数。在效率上，虚函数的效率要低很多。
说明2：
出于效率考虑，没有必要将所有成员函数都声明为虚函数


        对象中的VPTR指针什么时候被初始化？

对象在创建的时,由编译器对VPTR指针进行初始化
        只有当对象的构造完全结束后VPTR的指向才最终确定
父类对象的VPTR指向父类虚函数表
子类对象的VPTR指向子类虚函数表(创建子类对象时先执行父类的构造函数,子类的虚函数表指针先指向父类的虚函数表,子类对象创建后再指向子类的虚函数表)


联编是指一个计算机程序的不同部分彼此关联的过程。按照联编所进行的阶段不同，可分为两种不同的联编方法：静态联编和动态联编
        静态联编：
是指联编工作在编译阶段完成的，这种联编过程是在程序运行之前完成的，又称为早期联编。要实现静态联编，在编译阶段就
        必须确定程序中的操作调用（如函数调用）与执行该操作代码间的关系，确定这种关系称为束定，在编译时的束定称为静态束定。
静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高，但灵活性差。

动态联编:
是指联编在程序运行时动态地进行，根据当时的情况来确定调用哪个同名函数，实际上是在运行时虚函数的实现。
这种联编又称为晚期联编，或动态束定。动态联编对成员函数的选择是基于对象的类型，针对不同的对象类型将做出不同的编译结果。
C++中一般情况下的联编是静态联编，但是当涉及到多态性和虚函数时应该使用动态联编。动态联编的优点是灵活性强，但效率低

        final修饰类:该类不能被继承
        final修饰虚函数:该函数不能被重写

        override明确地表示一个函数是对基类中一个虚函数的重写
*/
#include <iostream>
#include <string>
using namespace std;

class Printer
{
public:
    virtual //虚函数可实现多态
    void  print(string s)
    {
        cout << "Printer:" <<s << endl;
    }

    virtual //纯虚函数,带有纯虚函数的类成为抽象类,抽象类不能实例化
    void  virtualPrint(string s) = 0;
    virtual ~Printer()= default;
};
class MyPrinter : public Printer //继承父类必须重写纯虚函数
{
public:
    void print(string s) override
    {
        cout << "MyPrinter:" << s << endl;
    }
    void virtualPrint(string s) override
    {
        cout << "MyPrinter VirtualPrint:" << s << endl;
    }
};

void testVirtual()
{
    Printer *p = new MyPrinter();
    p->print("hello virtual");
    p->virtualPrint("hello ");
    delete p;
}
int main20()
{
    testVirtual();
    return 0;
}



